Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | import { NextResponse } from 'next/server' import { withAuth } from '@/lib/auth/withAuth' import { canPerformAction, isParentOf } from '@/lib/classroom' import { getSessionPlan } from '@/lib/curriculum' import { createSessionShare, getActiveSharesForSession, revokeSessionShare, type ShareDuration, } from '@/lib/session-share' import { getShareUrl } from '@/lib/share/urls' import { getUserId } from '@/lib/viewer' /** * POST /api/sessions/[sessionId]/share * Create a new share link for a session * * Body: { expiresIn: '1h' | '24h' } * Returns: { token, url, expiresAt } */ export const POST = withAuth(async (request, { params }) => { const { sessionId } = (await params) as { sessionId: string } try { // Get current user const userId = await getUserId() if (!userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } // Get the session to find the player ID const session = await getSessionPlan(sessionId) if (!session) { return NextResponse.json({ error: 'Session not found' }, { status: 404 }) } // Check authorization - only parents can create share links (not teachers) const isParent = await isParentOf(userId, session.playerId) if (!isParent) { return NextResponse.json({ error: 'Only parents can create share links' }, { status: 403 }) } // Parse request body const body = await request.json() const expiresIn = body.expiresIn as ShareDuration if (expiresIn !== '1h' && expiresIn !== '24h') { return NextResponse.json({ error: 'Invalid expiresIn value' }, { status: 400 }) } // Create the share const share = await createSessionShare(sessionId, session.playerId, userId, expiresIn) // Build the full URL using the share URL helper (handles env vars correctly) const url = getShareUrl('observe', share.id) return NextResponse.json({ token: share.id, url, expiresAt: share.expiresAt.getTime(), }) } catch (error) { console.error('Error creating session share:', error) return NextResponse.json({ error: 'Failed to create share link' }, { status: 500 }) } }) /** * GET /api/sessions/[sessionId]/share * List all active shares for a session */ export const GET = withAuth(async (_request, { params }) => { const { sessionId } = (await params) as { sessionId: string } try { // Get current user const userId = await getUserId() if (!userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } // Get the session to find the player ID const session = await getSessionPlan(sessionId) if (!session) { return NextResponse.json({ error: 'Session not found' }, { status: 404 }) } // Check authorization const canObserve = await canPerformAction(userId, session.playerId, 'observe') if (!canObserve) { return NextResponse.json({ error: 'Not authorized' }, { status: 403 }) } // Get active shares const shares = await getActiveSharesForSession(sessionId) return NextResponse.json({ shares: shares.map((s) => ({ token: s.id, expiresAt: s.expiresAt instanceof Date ? s.expiresAt.getTime() : s.expiresAt, viewCount: s.viewCount, createdAt: s.createdAt instanceof Date ? s.createdAt.getTime() : s.createdAt, })), }) } catch (error) { console.error('Error listing session shares:', error) return NextResponse.json({ error: 'Failed to list shares' }, { status: 500 }) } }) /** * DELETE /api/sessions/[sessionId]/share?token=xxx * Revoke a specific share link */ export const DELETE = withAuth(async (request, { params }) => { const { sessionId } = (await params) as { sessionId: string } const token = request.nextUrl.searchParams.get('token') if (!token) { return NextResponse.json({ error: 'Token required' }, { status: 400 }) } try { // Get current user const userId = await getUserId() if (!userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } // Get the session to find the player ID const session = await getSessionPlan(sessionId) if (!session) { return NextResponse.json({ error: 'Session not found' }, { status: 404 }) } // Check authorization - only parents can revoke share links const isParent = await isParentOf(userId, session.playerId) if (!isParent) { return NextResponse.json({ error: 'Only parents can revoke share links' }, { status: 403 }) } // Revoke the share await revokeSessionShare(token) return NextResponse.json({ success: true }) } catch (error) { console.error('Error revoking session share:', error) return NextResponse.json({ error: 'Failed to revoke share' }, { status: 500 }) } }) |